page.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. "use client";
  2. import { useState, useEffect, useCallback } from "react";
  3. import { useParams, useRouter } from "next/navigation";
  4. import { Button } from "@/components/ui/button";
  5. import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
  6. import { Badge } from "@/components/ui/badge";
  7. import { Plus, ArrowLeft, Edit, Trash2 } from "lucide-react";
  8. import { Skeleton } from "@/components/ui/skeleton";
  9. import { AddFieldDialog } from "@/app/components/layout-configurations/AddFieldDialog";
  10. import { EditFieldDialog } from "@/app/components/layout-configurations/EditFieldDialog";
  11. import { DeleteFieldDialog } from "@/app/components/layout-configurations/DeleteFieldDialog";
  12. import { getLayoutSectionFields, getSection } from "@/app/actions/layout-configurations";
  13. interface LayoutSectionField {
  14. id: number;
  15. name: string;
  16. dataType: string;
  17. dataTypeFormat: string | null;
  18. cellPosition: string;
  19. importTableColumnName: string;
  20. importColumnOrderNumber: number;
  21. }
  22. interface LayoutSection {
  23. id: number;
  24. name: string;
  25. type: string;
  26. sheetName: string;
  27. tableName: string;
  28. }
  29. export default function SectionFieldsPage() {
  30. const params = useParams();
  31. const router = useRouter();
  32. const sectionId = parseInt(params.id as string);
  33. const [section, setSection] = useState<LayoutSection | null>(null);
  34. const [fields, setFields] = useState<LayoutSectionField[]>([]);
  35. const [loading, setLoading] = useState(true);
  36. const [addDialogOpen, setAddDialogOpen] = useState(false);
  37. const [editDialogOpen, setEditDialogOpen] = useState(false);
  38. const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
  39. const [selectedField, setSelectedField] = useState<LayoutSectionField | null>(null);
  40. const loadSectionData = useCallback(async () => {
  41. try {
  42. setLoading(true);
  43. const [sectionData, fieldsData] = await Promise.all([
  44. getSection(sectionId),
  45. getLayoutSectionFields(sectionId)
  46. ]);
  47. if (sectionData.success && sectionData.data) {
  48. setSection(sectionData.data);
  49. } else {
  50. setSection(null);
  51. }
  52. if (fieldsData.success && fieldsData.data) {
  53. setFields(fieldsData.data);
  54. } else {
  55. setFields([]);
  56. }
  57. } catch (error) {
  58. console.error("Error loading section data:", error);
  59. } finally {
  60. setLoading(false);
  61. }
  62. }, [sectionId]);
  63. useEffect(() => {
  64. loadSectionData();
  65. }, [sectionId, loadSectionData]);
  66. const handleFieldAdded = () => {
  67. loadSectionData();
  68. setAddDialogOpen(false);
  69. };
  70. const handleFieldUpdated = () => {
  71. loadSectionData();
  72. setEditDialogOpen(false);
  73. };
  74. const handleFieldDeleted = () => {
  75. loadSectionData();
  76. setDeleteDialogOpen(false);
  77. };
  78. const openEditDialog = (field: LayoutSectionField) => {
  79. setSelectedField(field);
  80. setEditDialogOpen(true);
  81. };
  82. const openDeleteDialog = (field: LayoutSectionField) => {
  83. setSelectedField(field);
  84. setDeleteDialogOpen(true);
  85. };
  86. if (loading) {
  87. return (
  88. <div className="container mx-auto py-6 space-y-6">
  89. <Skeleton className="h-8 w-64" />
  90. <Skeleton className="h-32 w-full" />
  91. <Skeleton className="h-64 w-full" />
  92. </div>
  93. );
  94. }
  95. if (!section) {
  96. return (
  97. <div className="container mx-auto py-6">
  98. <Card>
  99. <CardContent className="pt-6">
  100. <p className="text-center text-muted-foreground">Section not found</p>
  101. </CardContent>
  102. </Card>
  103. </div>
  104. );
  105. }
  106. return (
  107. <div className="container mx-auto py-6 space-y-6">
  108. <div className="flex items-center gap-4">
  109. <Button
  110. variant="ghost"
  111. size="sm"
  112. onClick={() => router.back()}
  113. >
  114. <ArrowLeft className="h-4 w-4 mr-2" />
  115. Back
  116. </Button>
  117. </div>
  118. <Card>
  119. <CardHeader>
  120. <div className="flex justify-between items-start">
  121. <div>
  122. <CardTitle>Section: {section.name}</CardTitle>
  123. <CardDescription>
  124. Manage fields for this section in the layout configuration
  125. </CardDescription>
  126. </div>
  127. <div className="flex gap-2">
  128. <Badge variant="secondary">{fields.length} fields</Badge>
  129. <Badge variant="outline">{section.type}</Badge>
  130. </div>
  131. </div>
  132. </CardHeader>
  133. <CardContent>
  134. <div className="grid gap-4">
  135. <div>
  136. <span className="font-medium">Sheet:</span> {section.sheetName}
  137. </div>
  138. <div>
  139. <span className="font-medium">Table:</span> {section.tableName}
  140. </div>
  141. </div>
  142. </CardContent>
  143. </Card>
  144. <div className="space-y-4">
  145. <div className="flex justify-between items-center">
  146. <h2 className="text-2xl font-semibold">Section Fields</h2>
  147. <Button size="sm" onClick={() => setAddDialogOpen(true)}>
  148. <Plus className="h-4 w-4 mr-2" />
  149. Add Field
  150. </Button>
  151. </div>
  152. {fields.length === 0 ? (
  153. <Card>
  154. <CardContent className="pt-6">
  155. <p className="text-center text-muted-foreground">
  156. No fields found for this section
  157. </p>
  158. </CardContent>
  159. </Card>
  160. ) : (
  161. <div className="grid gap-4">
  162. {fields.map((field) => (
  163. <Card key={field.id} className="hover:shadow-md transition-shadow">
  164. <CardContent className="pt-6">
  165. <div className="flex justify-between items-start">
  166. <div className="space-y-2">
  167. <div>
  168. <span className="font-medium">{field.name}</span>
  169. <Badge variant="outline" className="ml-2 text-xs">
  170. {field.dataType}
  171. </Badge>
  172. </div>
  173. <div className="text-sm text-muted-foreground">
  174. <div>Position: {field.cellPosition}</div>
  175. <div>Column: {field.importTableColumnName}</div>
  176. <div>Order: {field.importColumnOrderNumber}</div>
  177. </div>
  178. </div>
  179. <div className="flex gap-2">
  180. <Button
  181. variant="ghost"
  182. size="sm"
  183. onClick={() => openEditDialog(field)}
  184. >
  185. <Edit className="h-4 w-4" />
  186. </Button>
  187. <Button
  188. variant="ghost"
  189. size="sm"
  190. onClick={() => openDeleteDialog(field)}
  191. >
  192. <Trash2 className="h-4 w-4" />
  193. </Button>
  194. </div>
  195. </div>
  196. </CardContent>
  197. </Card>
  198. ))}
  199. </div>
  200. )}
  201. </div>
  202. <AddFieldDialog
  203. sectionId={sectionId}
  204. open={addDialogOpen}
  205. onOpenChange={setAddDialogOpen}
  206. onSuccess={handleFieldAdded}
  207. />
  208. <EditFieldDialog
  209. field={selectedField}
  210. open={editDialogOpen}
  211. onOpenChange={setEditDialogOpen}
  212. onSuccess={handleFieldUpdated}
  213. />
  214. <DeleteFieldDialog
  215. field={selectedField}
  216. open={deleteDialogOpen}
  217. onOpenChange={setDeleteDialogOpen}
  218. onSuccess={handleFieldDeleted}
  219. />
  220. </div>
  221. );
  222. }